package com.client.koshercook.server;


import java.util.List;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.gwt.user.client.rpc.IsSerializable;

/** Creating an object with Key of a recipe and its weight
 * Calculating the weight is implemented in this class*/
@PersistenceCapable
public class KeyWithWeight implements Comparable<KeyWithWeight> ,IsSerializable{
    @PrimaryKey
    @Persistent
	private Key key;
	
    private RecipeInfo recipe;
    
	@Persistent
	private int weight; //the higher the weight - the better priority to show user. (might be negative)
	
	@Persistent
	private String alsoNeed; //the string of ingredients missing

	
	/** Calculating the recipe weight if user have ings ingredients
	 * @pre ings,recipe != null
	 * second time*/
	public KeyWithWeight(RecipeInfo recipe, List<Key> ings,Accounts acc) {
		this.key = recipe.getKey();
		this.recipe = recipe;
		weight=0;

		alsoNeed = "";
		//calculating according to ings have/missing and save the missing
		for(Key x : recipe.getIngredients()) {
			if(ings.contains(x)) weight = weight+6; //have main ing
			else if(recipe.hasAlternativeFor(x)) {
				for(Key x2 : recipe.getAllAltersFor(x)) { //have alter ing inserted by the user
					if(ings.contains(x2)) {
						weight = weight+5;
						break;
					}
				}
			}
			else{
				//if we are here, we have not found recIng in client's ingredients
				weight = weight-9;
				alsoNeed += x.getName() + ", ";
			}
		}
				
			//according to other recipe statistics
			weight = weight + recipe.getNumOfFavorites();
			
			int graders = recipe.getNumOfGraders();
			if(graders>4)
				weight = weight + 10*(recipe.getGrade()-3);
			else
				weight = weight + 2*graders*(recipe.getGrade()-3);
			
			
			//according to user's preferances
			if(acc!=null) {
				int myGrade = acc.getRankForRecipe(recipe.getLink());
				if(myGrade==1) weight = weight - 40;
				else if(myGrade==2) weight = weight - 20;
				else if(myGrade==4) weight = weight + 5;
				else if(myGrade==5) weight = weight + 10;
				
				int uploaderPopularity = acc.getUploaderPopularity(recipe.getUploader());
				int numFavs = acc.getFavorites_name().size();
				if(numFavs<4) weight = weight + uploaderPopularity*5;
				else if(numFavs<7) weight = weight + uploaderPopularity*4;
				else if(numFavs<12) weight = weight + uploaderPopularity*3;
				else if(numFavs<23) weight = weight + uploaderPopularity*2;
				else weight = weight + uploaderPopularity;
			}
	}
	
	//first time, save time
	public KeyWithWeight(RecipeInfo recipe, int ingL,Accounts acc) {
		this.recipe = recipe;
		this.key = recipe.getKey();
		weight=0;
		//int ingL = ings.size();
			int ratio;
			if (ingL<3)
				ratio = 1;
			else
				ratio = ingL/3;
				
			weight= weight + (-9*(recipe.getIngredients().size()-ratio)+6*ratio)/3;  //try to estimate the final count 
			
			//according to other recipe statistics
			weight = weight + recipe.getNumOfFavorites();
			
			int graders = recipe.getNumOfGraders();
			if(graders>4)
				weight = weight + 10*(recipe.getGrade()-3);
			else
				weight = weight + 2*graders*(recipe.getGrade()-3);
			
			
			//according to user's preferances
			if(acc!=null) {
				int myGrade = acc.getRankForRecipe(recipe.getLink());
				if(myGrade==1) weight = weight - 40;
				else if(myGrade==2) weight = weight - 20;
				else if(myGrade==4) weight = weight + 5;
				else if(myGrade==5) weight = weight + 10;
				
				int uploaderPopularity = acc.getUploaderPopularity(recipe.getUploader());
				int numFavs = acc.getFavorites_name().size();
				if(numFavs<4) weight = weight + uploaderPopularity*5;
				else if(numFavs<7) weight = weight + uploaderPopularity*4;
				else if(numFavs<12) weight = weight + uploaderPopularity*3;
				else if(numFavs<23) weight = weight + uploaderPopularity*2;
				else weight = weight + uploaderPopularity;
			}
	}
	
	/**get the key of the recipe*/
	public Key getKey() {
		return key;
	}
	
	/**get the key of the recipe*/
	public RecipeInfo getRecipe() {
		return recipe;
	}
	
	/**get the calculated weight of the recipe*/
	public int getWeight() {
		return weight;
	}
	
	public void calculateFull(List<Key> ings){
		int ratio;
		int ingL = ings.size();
		if (ingL<3)
			ratio = 1;
		else
			ratio = ingL/3;
			
		weight = weight - (-9*(recipe.getIngredients().size()-ratio)+6*ratio)/3;  //undo the estimatation
		
		alsoNeed = "";
		//calculating according to ings have/missing and save the missing
		for(Key x : recipe.getIngredients()) {
			if(ings.contains(x)) weight = weight+6; //have main ing
			else if(recipe.hasAlternativeFor(x)) {
				for(Key x2 : recipe.getAllAltersFor(x)) { //have alter ing inserted by the user
					if(ings.contains(x2)) {
						weight = weight+5;
						break;
					}
				}
			}
			else{
				//if we are here, we have not found recIng in client's ingredients
				weight = weight-9;
				alsoNeed += x.getName() + ", ";
			}
		}
	}
	
	/** get the also need string*/
	public String getAlsoNeed() {
		return alsoNeed;
	}
	
	/**change the weight of the recipe*/
	public void setWeight(int weight) {
		this.weight = weight;
	}
	
	/**compare function for sorting in decreasing way according to weight
	 * @example Collections.sort(list{4,7,2}) gives list{7,4,2}
	 */
	public int compareTo(KeyWithWeight other) {
		if(weight>other.getWeight()) return -1;
		if(weight<other.getWeight()) return 1;
		return recipe.getKey().compareTo(other.getRecipe().getKey());
		}
}
